package com.altran.mirth.migration.v211;

import static com.altran.mirth.migration.v211.MigrationComment.getCategoryTags;
import static com.altran.mirth.migration.v211.MigrationComment.getComment;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

import com.altran.mirth.migration.v211.MigrationComment.Category;
import com.altran.mirth.migration.v211.MigrationComment.CommentType;
import com.altran.mirth.migration.v211.utils.Logger;

/**
 * Project :  Mirth
 * Author :   Altran
 *  *
 * Copyright (C) 2011 - Altran 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */

public class Migration {
	
	/* *********************************************************************** */
	
	public static final boolean PROCESS_BATCH 				= false;
	public static final boolean PROCESS_DEPRECATED 			= false;
	public static final boolean PROCESS_USERDEFINED 		= false;
	public static final boolean PROCESS_VARIABLES_LIST 		= true;
	public static final boolean PROCESS_VARIABLES_REPLACE 	= false;
	
	File inputFile = new File("Mirth_Config/2011-07-22 Mirth Backup_Step_3.xml");
	File outputFile = new File("Mirth_Config/2011-07-22 Mirth Backup_Step_4.xml");

	/* *********************************************************************** */
	
	public final static String DEPLOY_SCRIPT_START_TAG = "<deployScript>";
	public final static String DEPLOY_SCRIPT_END_TAG = "</deployScript>";
	
	public final static String CHANNEL_START_TAG = "<channel>";
	public final static String CHANNEL_END_TAG = "</channel>";

	public final static String CODE_TEMPLATE_START_TAG = "<codeTemplate>";
	public final static String CODE_TEMPLATE_END_TAG = "</codeTemplate>";
	
	public final static String CHANNEL_NAME_START_TAG = "<name>";
	public final static String CHANNEL_NAME_END_TAG = "</name>";

	public final static String RETURN_STATEMENT = "return;";
	
	public final static String LOG_DEPLOYED_CHANNEL_CMD = "logChannelDeployment(channelId);";

	/* *********************************************************************** */

	Logger logger;

	HashMap<String, BufferedWriter> listOfChannelLogs = new HashMap<String, BufferedWriter>();
	ArrayList<DeprecatedFunction> 	listOfDeprecated;
	ArrayList<UserFunction> 		listOfUserFunction;
	ArrayList<Variable> 			listOfVariables;
	ArrayList<ReplacedVariable> 	listOfReplacedVariables;
	ArrayList<Channel> 				listOfChannels;
	int nbDeployModified = 0;
	
	public Migration(){
		
		// ************* Init *************
		logger = Logger.getInstance();
		
		logger.setInputFile(inputFile);
		logger.setOutputFile(outputFile);
		logger.initLog();
		
		loadDataFromExternalFiles();
		
		listOfChannels = new ArrayList<Channel>();

		boolean inDeployScript = false;
		boolean inCodeTemplate = false;
		boolean inChannel = false;
		boolean inComment = false;
		boolean channelNameFound = false;
		String currentChannelName = "NONE";
		Channel currentChannel = new Channel();
		
		try {
			// ************* Check if source file exists *************
			if ( inputFile.exists() && inputFile.isFile()  ){				
				System.out.println("Opening file ...");
				BufferedReader bfr = new BufferedReader(new FileReader(inputFile));
				BufferedWriter bfw = new BufferedWriter(new FileWriter(outputFile));
				
				int oldLineNumber = 0;
				int newLineNumber = 1;
				String line = "";
				String lineToWrite = "";
				
				// ************* Browse source file and start processing rules *************
				System.out.println("	Reading file ...");
				while ( line  != null ){
					lineToWrite = line;
					
					// ************* Check which markup we are opening ************* 
					if ( line.contains(DEPLOY_SCRIPT_START_TAG) ){
						inDeployScript = true;
					}
					if ( line.contains(CHANNEL_START_TAG) ){
						inChannel = true;
					}
					if ( line.trim().startsWith("/*") ){
						inComment = true;
					}
					if ( line.trim().endsWith("*/") ){
						inComment = false;
					}
					if ( line.contains(CODE_TEMPLATE_START_TAG) ){
						inCodeTemplate = true;
					}
					
					if ( inChannel && !channelNameFound &&  line.trim().startsWith(CHANNEL_NAME_START_TAG)){
						currentChannelName = line.trim().replace(CHANNEL_NAME_START_TAG, "");
						currentChannelName = currentChannelName.replace(CHANNEL_NAME_END_TAG, "");
						channelNameFound = true;
						currentChannel = new Channel(currentChannelName);
					}
					
					// ************* Add log trace in DeployScript *************
					if ( inDeployScript && PROCESS_BATCH ){
						if ( line.contains(RETURN_STATEMENT) ){

							lineToWrite = line.replace(RETURN_STATEMENT, LOG_DEPLOYED_CHANNEL_CMD+ getComment(CommentType.DEPLOY_TRACE, true)+RETURN_STATEMENT);

							logger.logChange(oldLineNumber, newLineNumber, line, lineToWrite, "Adding a deploy log call", CommentType.DEPLOY_TRACE.getTag(), currentChannelName);
							newLineNumber++;
							nbDeployModified++;
						}
					}
					
					// ************* Deprecated Functions *************
					if ( PROCESS_DEPRECATED ){
						deprecatedFunctionLoop:
							for ( DeprecatedFunction func : listOfDeprecated ){
							
								if ( line.toUpperCase().contains(func.getDeprecatedPattern().toUpperCase()) ){
									if (func.isReplaceable()){

										lineToWrite = line.replace(func.getDeprecatedPattern(), func.getNewPattern()) + getComment(CommentType.DEPRECATED_REPLACED,  func.getDeprecatedPattern(), false);
										logger.logChange(oldLineNumber, newLineNumber, line, lineToWrite, "Replacing a deprecated function(#"+func.getID()+" : "+func.getName()+")", CommentType.DEPRECATED_REPLACED.getTag(), currentChannelName);

									}else{
										lineToWrite = line + getComment(CommentType.DEPRECATED_NON_REPLACED,  func.getNewPattern(), false);
										logger.logChange(oldLineNumber, newLineNumber, line, lineToWrite, "A function(#"+func.getID()+" : "+func.getName()+") need to be replaced", CommentType.DEPRECATED_NON_REPLACED.getTag(), currentChannelName);

									}
									func.addOccurency();
									currentChannel.addChange(func);
									break deprecatedFunctionLoop;
								}	
							}
						
					}

					// ************* User-Defined Functions *************
					if ( PROCESS_USERDEFINED ){
						userFunctionLoop:
							for ( UserFunction func : listOfUserFunction ){
							
								for ( Pattern pattern : func.getListOfPatterns()) {

									if ( line.toUpperCase().contains(pattern.getOldPattern().toUpperCase()) && ! line.trim().startsWith("/*") && ! line.trim().startsWith("//") && ! inCodeTemplate && ! inComment ){
										if (pattern.isReplaceable()){
											int start = line.toUpperCase().indexOf(pattern.getOldPattern().toUpperCase());
											int length = pattern.getOldPattern().length();
											
											lineToWrite = line.substring(0, start)+pattern.getNewPattern()+line.substring((start+length)) + getComment(CommentType.USERDEFINED_REPLACED,  pattern.getOldPattern(), false);
											logger.logChange(oldLineNumber, newLineNumber, line, lineToWrite, "Adding a user-defined function(#"+func.getID()+" : "+func.getName()+")", CommentType.USERDEFINED_REPLACED.getTag(), currentChannelName);

										}else{
											lineToWrite = line + getComment(CommentType.USERDEFINED_NON_REPLACED,  pattern.getNewPattern(), false);
											logger.logChange(oldLineNumber, newLineNumber, line, lineToWrite, "A user-defined function(#"+func.getID()+" : "+func.getName()+") could be used", CommentType.USERDEFINED_NON_REPLACED.getTag(), currentChannelName);

										}
										pattern.addOccurency();
										currentChannel.addChange(func);
										break userFunctionLoop;
									}
								}
							}						
					}
					
					// ************* Variables to replaced *************
					if ( PROCESS_VARIABLES_REPLACE ){
						replaceVariablesLoop:
							for ( ReplacedVariable var : listOfReplacedVariables ){
							
								if ( line.toUpperCase().contains(var.getOldName().toUpperCase())  && ! line.trim().startsWith("/*") && ! line.trim().startsWith("//") ){
									if (var.isReplaceable()){

										lineToWrite = line.replace(var.getOldName(), var.getNewName()) + getComment(CommentType.VARIABLES_REPLACED,  var.getOldName(), false);
										logger.logChange(oldLineNumber, newLineNumber, line, lineToWrite, "Replacing a variable (#"+var.getID()+" : "+var.getName()+")", CommentType.VARIABLES_REPLACED.getTag(), currentChannelName);

									}else{
										lineToWrite = line + getComment(CommentType.VARIABLES_NON_REPLACED,  var.getNewName(), false);
										logger.logChange(oldLineNumber, newLineNumber, line, lineToWrite, "A variable (#"+var.getID()+" : "+var.getName()+") could to be replaced", CommentType.VARIABLES_NON_REPLACED.getTag(), currentChannelName);

									}
									var.addOccurency();
									currentChannel.addChange(var);
									break replaceVariablesLoop;
								}	
							}
					}
					
					if ( PROCESS_VARIABLES_LIST ){
							// ************* Variables *************
							variablesLoop:
							for ( Variable var : listOfVariables ){
							
								if ( line.toUpperCase().contains(var.getOldName().toUpperCase())  && ! line.trim().startsWith("/*") && ! line.trim().startsWith("//") && ! inComment && ! inCodeTemplate ){

									logger.logChange(oldLineNumber, oldLineNumber, var.getOldName(), line, "Variable (#"+var.getID()+" : "+var.getName()+")", CommentType.VARIABLES_LISTED.getTag(), currentChannelName);

									var.addOccurency();
									currentChannel.addChange(var);
									break variablesLoop;
								}	
							}
					}

					// ************* Check which markup we are closing *************
					if ( line.contains(DEPLOY_SCRIPT_END_TAG) ){
						inDeployScript = false;
					}
					if ( line.contains(CODE_TEMPLATE_END_TAG) ){
						inCodeTemplate = false;
					}
					if ( line.contains(CHANNEL_END_TAG) ){
						inChannel = false;
						channelNameFound = false;
						currentChannelName = "NONE";
						listOfChannels.add(currentChannel);
					}
					
					bfw.write(lineToWrite + MigrationComment.LINE_RETURN);
					oldLineNumber++;
					newLineNumber++;
					line = bfr.readLine();					
				}
				System.out.println("Reading complete");
				
				System.out.println("Closing files");
				bfr.close();
				bfw.flush();
				bfw.close();	
				
				// ************************** Results **************************
				generateResults();
				
				logger.finalizeLogs();
			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	

	private void loadDataFromExternalFiles(){
		
		// Load Deprecated Functions
		listOfDeprecated = DeprecatedFunction.loadAllFunctions();

		// Load User-Defined Functions
		listOfUserFunction = UserFunction.loadAllFunctions();
		
		// Load New Variables
		listOfVariables = Variable.loadAllVariables();

		// Load Variables to Replaced
		listOfReplacedVariables = ReplacedVariable.loadAllReplacedVariables();

	}
	
	
	private void generateResults(){
		// ************************** Results **************************
		logger.log("-- **************************************** --");
		logger.log("Add deploy trace : "+getCategoryTags(Category.BATCH));
		logger.log("	DeployScript modified 		: "+nbDeployModified);
		logger.log("Variables replaced : ");
		for ( Variable var : listOfReplacedVariables)
			logger.log(var.getResults());

		logger.log("List of Variables : ");
		for ( Variable var : listOfVariables)
			logger.log(var.getResults());

		logger.log("Deprecated Functions : "+getCategoryTags(Category.DEPRECATED));
		for ( DeprecatedFunction func : listOfDeprecated)
			logger.log(func.getResults());

		logger.log("User-Defined Functions : "+getCategoryTags(Category.USERDEFINED));
		for ( UserFunction func : listOfUserFunction)
			logger.log(func.getResults());

		logger.log("-- **************************************** --");
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new Migration();
	}

}
